﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using DBWTProject.models;
using System.Xml;
using System.Globalization;
using System.Xml.Linq;
using System.Diagnostics;

namespace DBWTProject.route.util
{
    public static class ChartUtil
    {
        /// <summary>
        /// Erreichnet das Höhen- und Geschwindigkeitsprofil. Beachte out-Paras.
        /// </summary>
        /// <param name="route"></param>
        /// <param name="elevationProfile">out para!</param>
        /// <param name="speedProfile">out para!</param>
        public static void CalculateProfiles(Route route, out List<Tuple<double, double>> elevationProfile, out List<Tuple<double, double>> speedProfile)
        {
            KmlParser parser = new KmlParser(route.RawKML);
            IList<Tuple<double, double, double>> coordinateList = parser.GetCoordinateList();
            IList<DateTime> timeStampList = parser.GetTimeStampList();

            elevationProfile = new List<Tuple<double, double>>();
            speedProfile = new List<Tuple<double, double>>();

            double runningDistance = 0;
            for (var x = 1; x < coordinateList.Count; ++x)
            {
                double distanceDiff = DistanceAlgorithm.DistanceBetweenPlaces(coordinateList[x - 1].Item1, coordinateList[x - 1].Item2, coordinateList[x].Item1, coordinateList[x].Item2);

                TimeSpan timeDiff = (timeStampList != null) ? timeStampList[x] - timeStampList[x - 1] : TimeSpan.MaxValue;

                runningDistance += distanceDiff;

                Tuple<double, double> speedTuple = new Tuple<double, double>(runningDistance, (distanceDiff / timeDiff.TotalHours));
                speedProfile.Add(speedTuple);

                Tuple<double, double> elevationTuple = new Tuple<double, double>(runningDistance, coordinateList[x].Item3);
                elevationProfile.Add(elevationTuple);
            }
        }

        /// <summary>
        /// Mergt beliebig viele Listen von X-Y-Paaren zusammen, sodass man ende die X-Werte in der richtigen Reihenfolge sind.
        /// Beispiel:
        /// aus [(1/4),(2/8), (4/5)]        y1-Liste                        (x,y1)-Paare
        /// und [(0.5/7),(3/9), (4.5/6)]    y2-Liste                        (x,y2)-Paare
        /// wird [(0,5/_/7),(1/4/_), (2/8/_), (3/_/9), (4/5/_), (4.5/_/6)]  (x,y1,y2)-Paare, sortiert nach x
        /// </summary>
        /// <param name="route"></param>
        /// <returns></returns>
        public static SortedDictionary<double, double?[]> GetMergedCoorMatrix(List<List<Tuple<double, double>>> toMergeList)
        {
            NumberFormatInfo numberFormatUS = new CultureInfo("en-US", false).NumberFormat;
            //Zunächst jede Liste normalisieren, d.h. 
            //aus [(1/4),(2/8), (4/5)]
            //wird [(1/4/_),(2/8/_), (4/5/_)]   (wenn 2 Listen zu mergen sind und dies y1 ist. Bei y2 ist entsprechend die y1-Stelle leer.
            int yTupelArraySize = toMergeList.Count;//sind zwei Listen zu mergen, muss die Ergebnismatrix Platz für 2 y-Tupel halten
            SortedDictionary<double, double?[]> resultMatrix = new SortedDictionary<double, double?[]>();//Ergebnismatrix
            for (int i = 0; i < toMergeList.Count; i++)
            {
                IList<Tuple<double, double>> xyPairList = toMergeList[i];
                //jedes x-y-Paar normalisieren (also zur x-y1-y2-yn-Tupel konvertieren) und in resultMatrix werfen (SortedDictionary sortiert selbst nach x)
                foreach (Tuple<double, double> xyPair in xyPairList)
                {
                    double xKey = Convert.ToDouble(xyPair.Item1, numberFormatUS);
                    double?[] yTupels = new double?[yTupelArraySize];//hält y1-y2-... Liste (wobei immer nur ein y davon gesetzt wird, rest ist null)
                    //yTupels füllen: an der stelle i mit dem wert, rest bleibt null
                    for (int j = 0; j < yTupelArraySize; j++)
                    {
                        yTupels[j] = i == j ? (double?) xyPair.Item2 : null;
                    }
                    // yTupels ist für y1: (1/4/_),(2/8/_), (4/5/_)
                    // yTupels ist für y2: (0.5/_/7),(3/_/9), (4.5/_/6)

                    //ABER: es kann doch sein, dass mal ein x zweimal vorkommt, dann muss natürlich das entsprechende yTupel um den neuen y-Wert erweitert werden.
                    if (resultMatrix.ContainsKey(xKey))
                    {
                        double?[] existingYTupel = resultMatrix[xKey];
                        existingYTupel[i] = xyPair.Item2;
                    }
                    else
                    {
                        resultMatrix.Add(xKey, yTupels);
                    }
                }
            }
            return resultMatrix;
        }

        /// <summary>
        /// Zu Testzwecken
        /// </summary>
        /// <param name="mergedCoorMatrix"></param>
        public static void PrintMatrix(SortedDictionary<double, string[]> mergedCoorMatrix)
        {
            foreach (KeyValuePair<double, string[]> entry in mergedCoorMatrix)
            {
                string[] yTupels = entry.Value;
                string ys = "";
                foreach (string y in yTupels)
                {
                    ys += y == null ? "null" : y;
                    ys += "/";
                }
                Debug.WriteLine(entry.Key + "/" + ys);
            }
        }
    }
}